/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pfc.generacion;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.ddlutils.model.Column;
import org.apache.ddlutils.model.Database;
import org.apache.ddlutils.model.Table;
import pfc.comun.IDEpfc;
import pfc.comun.Proyecto;
import pfc.modelocg.CGModel;
import pfc.modeloer.ERAttrib;
import pfc.modeloer.EREntity;
import pfc.modeloer.ERModel;
import pfc.utils.Utils;

/**
 *
 * @author xandru
 */
public class GeneradorDTO {

    Proyecto _proyecto;
    Database _dbmodel;
    ERModel _erModel;
    CGModel _cgModel;

    public GeneradorDTO(Proyecto pro, Database db, ERModel er, CGModel cg) {
        this._proyecto = pro;
        this._dbmodel = db;
        this._erModel = er;
        this._cgModel = cg;
    }

    public void generarDTOs(File srcPath) {
        IDEpfc.log("\n================= Generando clases DTO ========================");
        File f = new File(srcPath.getAbsolutePath() + File.separator + "dto");

        try {
            FileUtils.forceMkdir(f);
        } catch (IOException ex) {
            Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, ex);
        }

        generarDTOBase(f);

        for (EREntity e : this._erModel.getEntityList()) {
            generarDTOFile(f, e);
        }
    }

    private void generarDTOFile(File f, EREntity e) {
        
        String clase = StringUtils.capitalize(e.getName());
        IDEpfc.logTime("Generando clase " + clase);

        ArrayList<String> imports = new ArrayList<String>();
        ArrayList<String> fields = new ArrayList<String>();
        ArrayList<String> cl = new ArrayList<String>();
        ArrayList<String> methods = new ArrayList<String>();

        imports.add("package " + _proyecto.nombre + ".dto;");


        addImports(imports);
        addConstructor(methods, clase);

        for (ERAttrib a : e.getAttribs()) {
            addField(fields, imports, a);
            addSetter(methods, a);
            addGetter(methods, a);
        }

        setIdentity(methods, e);

        putProperty(methods, e);
        stringProperty(methods, e);

        stringDescription(methods, e);

        try {
            PrintWriter dto = new PrintWriter(new FileOutputStream(f.getAbsolutePath() + File.separator + clase + ".java"));

            for (String line : imports) {
                dto.println(line);
            }
            dto.println("public class " + clase + " extends BaseDTO implements Cloneable  {");
            for (String line : fields) {
                dto.println(line);
            }
            for (String line : methods) {
                dto.println(line);
            }
            dto.println("}");

            dto.flush();
            dto.close();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, ex);

        }
    }

    private void addImports(ArrayList<String> imports) {
        String[] aImportar = {"pfc.core.db.*",
            "org.apache.commons.beanutils.PropertyUtils",
            "org.apache.commons.lang.time.DateUtils",
            "java.beans.PropertyDescriptor",
            "java.lang.reflect.InvocationTargetException",
            " java.text.ParseException",
            "java.util.Date",
            "java.text.SimpleDateFormat", "java.sql.*"
        };

        for (String p : aImportar) {
            imports.add("import " + p + ";");
        }
        imports.add("\n");

    }

    private void addConstructor(ArrayList<String> metodos, String clase) {
        metodos.add("public " + clase + "() {\n}");
        metodos.add("\n");
    }

    private void addField(ArrayList<String> campos, ArrayList<String> imports, ERAttrib a) {
        campos.add("private " + Utils.getTipoJava(a.getTypeCode()) + " _" + a.getName() + ";");

    }

    private void addSetter(ArrayList<String> methods, ERAttrib a) {
        methods.add("public void set" + StringUtils.capitalize(a.getName()) + " (" + Utils.getTipoJava(a.getTypeCode()) + " value)");
        methods.add("{");
        methods.add("this._" + a.getName() + "= value" + ";");
        methods.add("}");
        methods.add("\n");
    }

    private void addGetter(ArrayList<String> methods, ERAttrib a) {
        methods.add("public " + Utils.getTipoJava(a.getTypeCode()) + " " + "get" + StringUtils.capitalize(a.getName()) + "()");
        methods.add("{");
        methods.add("return " + "_" + a.getName() + ";");
        methods.add("}");
        methods.add("\n");
    }

    private void setIdentity(ArrayList<String> methods, EREntity e) {
        Column[] pKeys = e.getTable().getPrimaryKeyColumns();       

        if (pKeys.length==0)
                return;

        if (pKeys.length == 1) {
            String metodoGetter = "get" + StringUtils.capitalize(pKeys[0].getName()) + "()";
            methods.add("public SinglePrimaryKey getIdentity()");
            methods.add("{");
            methods.add("SinglePrimaryKey pk = new SinglePrimaryKey(\"" + pKeys[0].getName() + "\"," + metodoGetter + ");");
            methods.add("return pk;");
            methods.add("}");
        } else {
            String metodoGetter = "get" + StringUtils.capitalize(pKeys[0].getName()) + "()";
            methods.add("public CompositePrimaryKey getIdentity()");
            methods.add("{");
            methods.add("CompositePrimaryKey pk = new CompositePrimaryKey(\"" + pKeys[0].getName() + "\"," + metodoGetter + ");");
            methods.add("return pk;");
            methods.add("}");
        }

        if (pKeys.length == 1) {
            String metodoSetter = "set" + StringUtils.capitalize(pKeys[0].getName()) + "()";
            methods.add("public void setIdentity(PrimaryKey key)");
            methods.add("{");
            methods.add("try {");
            methods.add("putPropertyValue(\""+pKeys[0].getName()+"\", key.getStringValue());");
            methods.add("} catch (Exception e){System.out.println(\"e.getMessage()\");}");
            methods.add("}");
        } else {
            String metodoSetter = "set" + StringUtils.capitalize(pKeys[0].getName()) + "()";
            methods.add("public void setIdentity(PrimaryKey key)");
            methods.add("{");
            methods.add("try {");
            methods.add("putPropertyValue(\""+pKeys[0].getName()+"\", key.getStringValue());");
             methods.add("} catch (Exception e){System.out.println(\"e.getMessage()\");}");
            methods.add("}");
        }


    }

    private void putProperty(ArrayList<String> methods, EREntity e) {
        String patrones = IDEpfc.getPropertyValue("patronesfecha");

        String[] patron = StringUtils.split(patrones, ";");

        methods.add("public boolean putPropertyValue(String property, String value) throws ParseException {");
        methods.add("PropertyDescriptor pd = null;");
//methods.add("System.out.println(\"kk1 \" + property + \" \" + value);");
        methods.add("try {");
//methods.add("System.out.println(\"kk2 \" + property + \" \" + value);");
        methods.add("pd = PropertyUtils.getPropertyDescriptor(this, property);");
//methods.add("System.out.println(\"kk3 \" + property + \" \" + value + \" \" + pd);");
        methods.add("}");
        methods.add("catch (Exception e) {");
        methods.add("System.out.println(\"Excepcion \" +  e.getClass().getCanonicalName() + \": \" + e.getMessage());");
        methods.add("return false;");
        methods.add("}");

        methods.add("if (pd==null) return false;");

//methods.add("System.out.println(\"kk4 \" + property + \" \" + value);");
        methods.add("Object castValue = null;");
//methods.add("System.out.println(\"kk5 \" + property + \" \" + value);");
        methods.add("if (pd.getPropertyType() == Integer.class) {");
        methods.add("    castValue = new Integer(value);");
        methods.add("}");
//methods.add("System.out.println(\"kk6 \" + property + \" \" + value);");
        methods.add("if (pd.getPropertyType() == Date.class) {");
        methods.add("SimpleDateFormat f = new SimpleDateFormat(\"dd/MM/yyyy\");");
        methods.add("f.setLenient(false);");
        methods.add("    castValue = f.parse(value);");
        methods.add("}");
//methods.add("System.out.println(\"kk7 \" + property + \" \" + value);");
        methods.add("if (pd.getPropertyType() == String.class) {");
        methods.add("    castValue = value;");
        methods.add("}");

//methods.add("System.out.println(\"kk8 \" + property + \" \" + value);");


        methods.add("try {");
//methods.add("System.out.println(\"kk10 \" + property + \" \" + value + \" <-value vs castValue -> \" + castValue);");
        methods.add("    PropertyUtils.setProperty(this, property, castValue);");
//methods.add("System.out.println(\"kk20 \" + property + \" \" + value + \" <-value vs castValue -> \" + castValue);");

        methods.add("    return true;");
        methods.add("} catch (Exception ex) {");
        methods.add("    System.out.println(ex.getMessage());");
        methods.add("    return false;");
        methods.add("}");
        methods.add("}");
    }

    private void stringProperty(ArrayList<String> methods, EREntity e) {
        String patrones = IDEpfc.getPropertyValue("patronesfecha");

        String[] patron = StringUtils.split(patrones, ";");

        methods.add("public String stringPropertyValue(String property) throws ParseException {");
        methods.add("PropertyDescriptor pd = null;");
//methods.add("System.out.println(\"kk1 \" + property);");
        methods.add("try {");
//methods.add("System.out.println(\"kk2 \" + property);");
        methods.add("pd = PropertyUtils.getPropertyDescriptor(this, property);");
//methods.add("System.out.println(\"kk3 \" + property + \" \" + pd);");
        methods.add("}");
        methods.add("catch (Exception e) {");
        //methods.add("System.out.println(\"Excepcion \" +  e.getClass().getCanonicalName() + \": \" + e.getMessage());");
        methods.add("System.out.println(\"Excepcion \" + pd);");
        methods.add("return \"\";");
        methods.add("}");

        methods.add("if (pd==null) return \"\";");

        methods.add("Object value = null;");
        methods.add("String castValue = null;");

        methods.add("try {");
        methods.add("value = PropertyUtils.getProperty(this,property);");
        methods.add("if (pd.getPropertyType() == Integer.class) {");
        methods.add("    castValue = ((Integer)value).toString();");
        methods.add("}");

        methods.add("if (pd.getPropertyType() == Date.class) {");
        methods.add("SimpleDateFormat sdf = new SimpleDateFormat(\"dd/MM/yyyy\");");
        methods.add("    castValue = sdf.format((Date)value);");
        methods.add("}");

        methods.add("if (pd.getPropertyType() == String.class) {");
        methods.add("    castValue = (String)value;");
        methods.add("}");

        methods.add("    return castValue;");
        methods.add("} catch (Exception ex) {");
        methods.add("    System.out.println(\" el campo \" + property +\" no tiene valor \" + ex);");
        methods.add("    return \"\";");
        methods.add("}");
        methods.add("}");
    }


    /*
    private void generarDTOBase(File f) {
    try {
    PrintWriter dto = new PrintWriter(new FileOutputStream(f.getAbsolutePath() + File.separator + "BaseDTO" + ".java"));

    dto.println("package " + _proyecto.nombre + ".dto;");
    dto.println("import java.text.ParseException;");

    dto.println("public abstract class BaseDTO {");
    dto.println("abstract public boolean putPropertyValue (String property, String value) throws ParseException;");
    dto.println("abstract public String stringPropertyValue(String property) throws ParseException;");

    dto.println("}");
    dto.flush();
    dto.close();
    } catch (FileNotFoundException ex) {
    Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, ex);

    }
    }
     */
    private void stringDescription(ArrayList<String> methods, EREntity e) {
        methods.add("public String stringDescription() throws ParseException {");
        methods.add("String value=\"\";");
        int i=0;
        for (String att : e.getRepresentativeAttibutes()) {
            i++;
            String metodo = "stringPropertyValue(\"" + att + "\")";
            methods.add("value +=\" \" + " + metodo + ";");
        }

        if (i==0)//no hay atributos representativos
        {
            methods.add("value += getIdentity().getStringValue() + \" (no se han encontrado atributos representativos\";");
        }

        methods.add("return value;");
        methods.add("}");

    }

    private void generarDTOBase(File f) {
        String clase = "BaseDTO";

        IDEpfc.logTime("Generando BaseDTO");

        try {
            PrintWriter dto = new PrintWriter(new FileOutputStream(f.getAbsolutePath() + File.separator + clase + ".java"));
            dto.println("package " + _proyecto.nombre + ".dto;\n");

            dto.println("import java.text.ParseException;");
            dto.println("import pfc.core.db.PrimaryKey;");
            dto.println("public abstract class BaseDTO {");
            dto.println("abstract public boolean putPropertyValue (String property, String value) throws ParseException;");
            dto.println("abstract public String stringPropertyValue(String property) throws ParseException;");
            dto.println("abstract public PrimaryKey getIdentity();");
            dto.println("abstract public String stringDescription() throws ParseException;");
            dto.println("}");

            dto.flush();
            dto.close();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
